package idalloc

import (
	"sync"

	"gitee.com/851537/gbase"
	"github.com/cockroachdb/errors"
)

// Allocator 高性能ID分配器
type Allocator struct {
	config Config

	// 本地缓存的未使用ID（最小堆）
	heap *gbase.MinHeap[int64]
	mu   sync.RWMutex

	// 批量获取大小
	//batchSize int
}

// NewAllocator 创建高性能ID分配器
func NewAllocator(config Config) *Allocator {
	if config.HeapSize <= 0 {
		config.HeapSize = 100 // 默认批量大小
	}
	// if config.SyncPeriod <= 0 {
	// 	config.SyncPeriod = 60 // 默认60秒同步一次
	// }

	return &Allocator{
		config: config,
		heap:   gbase.NewMinHeap[int64](config.HeapSize),
	}
}

// GetNextId 获取下一个可用的ID（高性能版本）
func (a *Allocator) GetNextId() (int64, error) {
	// 尝试从本地缓存获取
	a.mu.Lock()
	if !a.heap.IsEmpty() {
		id := a.heap.Pop()
		a.mu.Unlock()
		return id, nil
	}
	a.mu.Unlock()

	// 缓存为空，批量加载
	return a.refillCacheAndGetId()
}

// refillCacheAndGetId 重新填充缓存并获取ID
func (a *Allocator) refillCacheAndGetId() (int64, error) {
	a.mu.Lock()
	defer a.mu.Unlock()

	// 双重检查，避免重复填充
	if !a.heap.IsEmpty() {
		return a.heap.Pop(), nil
	}

	// 从存储获取批量ID（这里获取全部是为了支持简单的存储实现）
	var allUnusedIds []int64
	var err error
	if allUnusedIds, err = a.config.GetUnusedIds(); err != nil {
		return 0, errors.Wrap(err, "获取未使用的ID列表失败")
	}

	if len(allUnusedIds) == 0 {
		// 没有可用ID，尝试生成新ID
		if a.config.GenerateNewId != nil {
			return a.config.GenerateNewId()
		}
		return 0, errors.Newf("%s 无可用ID", a.config.Name)
	}

	// 计算要取出的数量：取 batchSize 个，但至少留一个返回
	takeCount := min(a.config.HeapSize, len(allUnusedIds))

	// 将取出的ID放入本地缓存（除了第一个，直接返回）
	var firstId int64
	for i := 0; i < takeCount; i++ {
		if i == 0 {
			firstId = allUnusedIds[i] // 第一个ID直接返回
		} else {
			a.heap.Push(allUnusedIds[i]) // 其余放入缓存
		}
	}

	// 更新远程存储（移除已取出的ID）
	remainingIds := allUnusedIds[takeCount:]
	if err := a.config.SetUnusedIds(remainingIds); err != nil {
		// 如果更新失败，清空本地缓存避免重复分配
		a.heap.Clear()
		return 0, errors.Wrap(err, "更新未使用的ID列表失败")
	}

	return firstId, nil
}

// RecycleId 回收ID（高性能版本）
func (a *Allocator) RecycleId(id int64) error {
	if err := a.ValidateId(id); err != nil {
		return err
	}

	// 直接加入本地缓存
	a.mu.Lock()
	a.heap.Push(id)
	a.mu.Unlock()

	// 异步同步到持久化存储
	// if a.config.PersistSync != nil {
	// 	gbase.Go(a.config.PersistSync)
	// }

	return nil
}

// ValidateId 验证ID是否在有效范围内
func (a *Allocator) ValidateId(id int64) error {
	if id < a.config.Range.Min || id > a.config.Range.Max {
		return errors.Newf("%s ID %d 超出有效范围 [%d, %d]",
			a.config.Name, id, a.config.Range.Min, a.config.Range.Max)
	}
	return nil
}

// InitUnusedIds 初始化未使用的ID列表（系统启动时调用）
func (a *Allocator) InitUnusedIds(unusedIds []int64) error {
	a.mu.Lock()
	defer a.mu.Unlock()
	a.heap.Clear()
	return a.config.SetUnusedIds(unusedIds)

	// // 获取所有已使用的ID
	// unusedIds, err := queryFunc()
	// if err != nil {
	// 	return errors.Wrap(err, "查询未使用的ID列表失败")
	// }
	// return a.config.SetUnusedIds(unusedIds)

	// 创建已使用ID的映射
	// usedIdMap := make(map[int64]bool)
	// for _, id := range usedIds {
	// 	if id >= a.config.Range.Min && id <= a.config.Range.Max {
	// 		usedIdMap[id] = true
	// 	}
	// }

	// // 查找所有未使用的ID
	// var unusedIds []int64
	// for id := a.config.Range.Min; id <= a.config.Range.Max; id++ {
	// 	if !usedIdMap[id] {
	// 		unusedIds = append(unusedIds, id)
	// 	}
	// }

	// // 保存到缓存
	// return a.config.SetUnusedIds(unusedIds)
}

// GetAvailableIdCount 获取可用的ID数量
func (a *Allocator) GetAvailableIdCount(countFunc func() (int64, error)) (int, error) {
	a.mu.RLock()
	defer a.mu.RUnlock()

	// 获取当前已使用的数量
	count, err := countFunc()
	if err != nil {
		return 0, errors.Wrap(err, "查询已使用的ID数量失败")
	}

	// 可用数量 = 总数量 - 已使用数量
	totalCount := a.config.Range.Max - a.config.Range.Min + 1
	available := int(totalCount) - int(count)
	return available, nil
}

// GetConfig 获取配置
func (a *Allocator) GetConfig() Config {
	return a.config
}

// GetStats 获取性能统计
// func (a *Allocator) GetStats() (cacheSize int, totalRange int64) {
// 	a.cacheMutex.RLock()
// 	cacheSize = a.localCache.Len()
// 	a.cacheMutex.RUnlock()

// 	totalRange = a.config.Range.Max - a.config.Range.Min + 1
// 	return
// }

// min 辅助函数,用于测试编译通过
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
